[#885] Bound the create of the tree catalog, and say when an engine has no bound to give - #1003
Open
vharseko wants to merge 1 commit into
Open
Conversation
…say when an engine has no bound to give The create table of the tree catalog is the DDL of this backend that reaches neither commitStatement() nor the drop loop: openTree() issues it on the catalog's own connection before it enrols the tree it is opening, so the bound of this branch never covered it. It is BULK, so it carries no query timeout, and the standing read bound of OpenIdentityPlatform#934 is lifted for the length of an unbounded statement - which left it the one statement of this backend with no bound of any kind, on the open path, inside the monitor every other thread of the storage queues on. It now runs under withDdlLockBound() like every other DDL, with the commit inside the bound: on postgres that commit is what ends the transaction a "set local" belongs to. That connection is not the pool's, so the two places claiming a connection reaching this code is pooled say what happens to the catalog's own instead - it is closed with the write that opened it. And an engine behind a driver this backend knows no lock setting for is now reported once rather than left silent. dialectOf() keys on the class name of the driver, so a mariadb, percona or aurora driver against a live mysql answers null - a session whose lock_wait_timeout is a year, which is the wait this bound exists to end. Leaving the bound off there stays; only the silence goes, for the reason the strict parsing of the property exists. CachedConnection cannot say it: it keys on the url, which such a driver reads as a mysql one. JDBCDdlLockBoundTestCase gains six cases: what postgres and mysql are told around the create of the catalog table, the lock it gives up on naming the property out to the operator, and the three placements of the report - the unknown engine, the bound nobody asked for, and the oracle left alone on purpose. The suite is mock-only and now stays that way: the storage of a case answers newStampConnection() itself rather than letting it reach DriverManager.
vharseko
force-pushed
the
issues/885-catalog-ddl-bound
branch
from
September 11, 2026 05:14
1c40760 to
a4b4934
Compare
Member
Author
|
@maximthomas rebased onto master now that #936 is in (21d03d5): the diff is the one commit a4b4934, two files, as the description said it would be. One conflict, in The run from the description, on the rebased branch: 306 tests, 0 failures, 0 errors, 0 skips - 306 rather than 271, these classes having gained cases on master since. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #885, on top of #936 (merged as 21d03d5):
withDdlLockBound()is that PR's code. Rebased onto master after that merge - the diff here is one commit, two files.Problem
#936 bounded the DDL of this backend by putting
withDdlLockBound()on the two paths its DDL goes through -commitStatement(sql, ddl==true)and the drop loop of a removed backend. There is a third, and it is neither of them.createCatalogTable()issues itscreate tableon the catalog's own connection, beforeopenTree()enrols the tree it is opening. That statement:StatementBound.BULK, so it carries no query timeout by design ([#877] Bound a statement of the JDBC backend by the class of the work it belongs to #882);So after #934 and #936 it is the one statement of this backend with no bound of any kind. Where it runs makes that worse rather than better: it is on the open path, and it runs inside
synchronized (catalogLock)- a wait there parks every other thread of the storage that wants the catalog, not only the one that queued.Nor does that wait need an exotic database. The method's own comment names the case: "a table that turned up between the lookup and this statement is what was wanted, whoever made it" - an offline tool beside a running server, the pair #888 is about. On postgres a second session creating that same table inside a transaction it has not committed makes this one wait on that transaction,
lock_timeoutbeing 0 by default; a lock held on the schema does the same.It arrived after both were written:
createCatalogTable()came with #893, which merged the same morning #934 did, and #936 took it in through its merge of master without the bound reaching it.Change
1. The create of the catalog table runs under
withDdlLockBound(), like every other DDL of this backend. The commit is inside the bound, because on postgres it is that commit which ends the transaction aset localbelongs to.Everything the bound leans on already works on this connection:
physical()passes a raw connection through,dialectOf()reads the driver name off it, andrestoreDdlLockBound()already asksinstanceof CachedConnectionbefore keeping a connection out of a pool. What did not fit is what two comments claim, this being the one connection reaching that code which was never in the pool: the javadoc ofwithDdlLockBound()and the "left behind" warning ofrestoreDdlLockBound()now say what becomes of the catalog's own - it is closed with the write that opened it, so a setting left on it reaches the rest of that write and nothing after it.A lock this create gives up on reaches the operator named:
gaveUpOnTheLock()rewrites it, the existingcatchcarries it into theStorageRuntimeExceptionsaying which table the backend wanted and why, and the property is in that chain. Unbounded, it arrived as a bare55P03inside a message about privileges - which is the wrong thing to go and check.2. An engine this backend knows no lock setting for is reported, once. The early return of
withDdlLockBound()had three reasons behind one condition; they are three conditions now, and only the middle one speaks:seconds<=0- the deployment asked for no bound, and there is nothing to say;dialect==null- reported, naming the class of the driver;ddlLockBoundSql(seconds,null)==null- oracle, left alone deliberately and documented as such.dialectOf()keys on the driver class name, so this is not the engine of an exotic database alone: MariaDB Connector/J - or a Percona- or Aurora-branded driver - against a live MySQL answers null here, and that is a session whoselock_wait_timeoutis a year, which is the wait this bound exists to end. Leaving the bound off there stays, andtestAnEngineThisBackendDoesNotKnowIsLeftAlonestill pins it: untested SQL is no thing to send a database on the path a backend opens by. Only the silence goes, for the reason the strict parsing of the property exists - a deployment that asked for a bound is never quietly left with none.reportUnknownDialect()inCachedConnectioncannot cover this one: it keys on the url, which such a driver reads as a mysql one, and it speaks of the connect bound andpool.timeout. The driver is named rather than the url, since the driver is what this reads and what a deployment would change - and a url carries the password of the account the backend works as.The javadoc of
ddl.lock.timeoutnow names this case beside the oracle exemption, that being what an operator reads after meeting a DDL which waited anyway.Testing
JDBCDdlLockBoundTestCasegains six cases: what postgres and what mysql are told around the create of the catalog table (the readback and the value given back among them), the lock it gave up on naming the property out to the caller, and the three placements of the report - the unknown engine, the bound nobody asked for, and the oracle left alone on purpose.306 tests, 0 failures, 0 errors, 0 skips, on the rebase onto master (21d03d5) - the two cases the last commit of #936 added to
JDBCDdlLockBoundTestCaseamong them.Passing is not enough on its own, so every guard was built with its defect put back:
testTheCreateOfTheCatalogTableIsBounded(theset local lock_timeout = 5000gone from what postgres was told),...GivesMysqlItsValueBack(expected [4] but found [2]) and...GaveUpOnNamesTheProperty(the lock reaches the operator as the bare 55P03 it arrived as)testAWaitNobodyAskedToBoundIsNotReportedAsAnUnknownEnginefails,expected [false] but found [true]testOracleIsNotReportedAsAnEngineThisBackendDoesNotKnowfails the same way, andtestAnEngineThisBackendDoesNotKnowIsReportedwith itOne thing the new cases needed, and the suite is the better for it: the storage of a case now answers
newStampConnection()itself. ReachingopenTree()means reaching the stamp of #866, whose connect would otherwise go toDriverManager- which registers every JDBC driver on the classpath. This suite needs no database and should touch none:reuseForks=falsekeeps that from reaching another class of this build, but it reaches one in an IDE, whereCatalogConnectionTestCaseneeds its own probe driver registered ahead of pgjdbc.Out of scope
ddl_lock_timeoutgives up at once by default, and ours would only loosen it.0default ofread.timeout([#885] Give a connection of the JDBC pool a read bound of its own, and take it off for a statement that carries none #934) and the unboundedbulk.timeout([#877] Bound a statement of the JDBC backend by the class of the work it belongs to #882), both shipped deliberately.create tableof the same name in an uncommitted transaction of a second session is postgres alone - mysql and oracle commit their DDL as they issue it - so a case injdbc/TestCasewould be a case about one engine wearing four engines' clothes. What each of the four is told around this create is pinned by the mock cases instead.